﻿using CNav.Business.Validation;
using CNav.Common;
using CNav.DataEntity;
using CNav.Domain;
using FluentValidation.Results;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Transactions;

namespace CNav.Business.Services
{
    public interface IBusinessAccountService
    {
        ResponseData<int> Register(RegisterBusiness registerBusiness);
        ResponseData<bool> AddUser(RegisterUserProfile registerUserProfile);
        ResponseData<List<UserProfile>> GetUserProfiles(bool includeAdmin);
        ResponseData<Domain.Business> GetBusiness(int id);
        ResponseData<Domain.Business> UpdateBusiness(Domain.Business business);

        ResponseData<List<UserProfileEx>> GetUserProfilesDetails(bool p);
    }

    public class BusinessAccountService : ServiceBase, IBusinessAccountService
    {
        public ResponseData<int> Register(RegisterBusiness registerBusiness)
        {
            var response = new ResponseData<int>();
            if (BusinessExist(registerBusiness.Email))
            {
                response.Error = new[] { "Email address already in use." };
                return response;
            }

            var validator = new RegisterBusinessValidation();
            ValidationResult validationResult = validator.Validate(registerBusiness);

            if (validationResult.IsValid)
            {
                var uEmail = registerBusiness.Email.ToUpper();
                var businessDto = new BusinessDto
                {
                    BusinessName = registerBusiness.BusinessName,
                    Email = uEmail,
                    CreatedOnUtc = DateTime.UtcNow,
                    FaxNumber = registerBusiness.FaxNumber,
                    PhoneNumber = registerBusiness.PhoneNumber,
                    LastActivityDateUtc = DateTime.UtcNow,
                    AddressID = 1
                };

                var userProfile = new UserProfileDto
                {
                    Title = string.Empty,
                    AccountExpireDate = DateTime.UtcNow.AddYears(1),
                    Business = businessDto,
                    Email = uEmail,
                    FaxNumber = businessDto.FaxNumber,
                    FirstName = businessDto.BusinessName,
                    IsBusinessAdmin = true,
                    LastName = string.Empty,
                    Password = Services.Get<ISecurityService>().Encrypt(registerBusiness.Password),
                    PhoneNumber = businessDto.PhoneNumber,
                    UserPosition = "Business",
                };

                using (var scope = new TransactionScope())
                {
                    Context.Repository<BusinessDto>().Add(businessDto);
                    Context.Repository<UserProfileDto>().Add(userProfile);
                    Context.Commit();

                    scope.Complete();
                    response.Data = businessDto.ID;
                    return response;
                }
            }

            response.Error = validationResult.ErrorList();
            return response;
        }

        public ResponseData<bool> AddUser(RegisterUserProfile user)
        {
            var response = new ResponseData<bool>();

            if (UserExist(user))
            {
                response.Error = new[] { "User email address already exist" };
                return response;
            }

            var validator = new RegisterUserProfileValidator();
            var validationResult = validator.Validate(user);

            if (validationResult.IsValid)
            {
                //TODO: Future design, Get Business specific User creation configuration from Database, i.e default PassExpireDate, AccountExpireDate etc settings 
                var userProfile = new UserProfileDto
                {
                    Title = user.Title,
                    AccountExpireDate = user.AccountExpireDate,
                    BusinessID = user.BusinessID,
                    Email = user.Email,
                    FaxNumber = user.FaxNumber,
                    FirstName = user.FirstName,
                    IsBusinessAdmin = false,
                    LastName = user.LastName,
                    Password = Services.Get<ISecurityService>().Encrypt(user.Password),
                    PhoneNumber = user.PhoneNumber,
                    UserPosition = user.UserPosition,
                    PassExpireDate = DateTime.UtcNow.AddYears(1)
                };

                Context.Repository<UserProfileDto>().Add(userProfile);
                Context.Commit();

                response.Data = true;
                return response;
            }

            response.Error = validationResult.ErrorList();
            return response;
        }

        public ResponseData<List<UserProfileEx>> GetUserProfilesDetails(bool includeAdmin)
        {
            var profiles = Context.Get<UserProfileDto>(u => (includeAdmin) || u.IsBusinessAdmin == includeAdmin).ToList();
            var response = new ResponseData<List<UserProfileEx>> { Data = new List<UserProfileEx>() };

            foreach (var profile in profiles)
            {
                var userProfile = new UserProfileEx();
                userProfile.ID = profile.ID;
                userProfile.BusinessID = profile.BusinessID;
                userProfile.IsBusinessAdmin = profile.IsBusinessAdmin ?? false;
                userProfile.UserPosition = profile.UserPosition;
                userProfile.PhoneNumber = profile.PhoneNumber;
                userProfile.FaxNumber = profile.FaxNumber;
                userProfile.Email = profile.Email;
                userProfile.Title = profile.Title;
                userProfile.FirstName = profile.FirstName;
                userProfile.LastName = profile.LastName;

                userProfile.IsLogOn = profile.IsLogOn;
                userProfile.LogOffDate = profile.LogOffDate;
                userProfile.LogOnAttempt = profile.LogOnAttempt;
                userProfile.LockOutExpireDate = profile.LockOutExpireDate;
                userProfile.PassExpireDate = profile.PassExpireDate;
                userProfile.AccountExpireDate = profile.AccountExpireDate;
                userProfile.MachineIP = profile.MachineIP;
                userProfile.Deleted = profile.Deleted;

                response.Data.Add(userProfile);
            }

            return response;
        }

        public ResponseData<List<UserProfile>> GetUserProfiles(bool includeAdmin)
        {
            var profiles = Context.Get<UserProfileDto>(u => (includeAdmin) || u.IsBusinessAdmin == includeAdmin).ToList();
            var response = new ResponseData<List<UserProfile>> { Data = new List<UserProfile>() };

            foreach (var profile in profiles)
            {
                var userProfile = new UserProfile();
                userProfile.ID = profile.ID;
                userProfile.BusinessID = profile.BusinessID ;
                userProfile.IsBusinessAdmin = profile.IsBusinessAdmin ?? false;
                userProfile.UserPosition = profile.UserPosition;
                userProfile.PhoneNumber = profile.PhoneNumber;
                userProfile.FaxNumber = profile.FaxNumber;
                userProfile.Email = profile.Email;
                userProfile.Title = profile.Title;
                userProfile.FirstName = profile.FirstName;
                userProfile.LastName = profile.LastName;

                response.Data.Add(userProfile);
            }

            return response;
        }

        public ResponseData<Domain.Business> GetBusiness(int id)
        {
            var response = new ResponseData<Domain.Business>();

            Domain.Business business = GetBusinessById(id);

            if (business != null)
            {
                response.Data = business;
                return response;
            }

            response.Error = new[] { "Invalid business id provided." };
            return response;
        }

        private Domain.Business GetBusinessById(int id)
        {
            var businessDto = Context.Get<BusinessDto>(b => b.ID == id).ToList().FirstOrDefault();
            if (businessDto != null)
            {
                var business = new Domain.Business();

                business.ID = businessDto.ID;
                business.BusinessName = businessDto.BusinessName;
                business.Email = businessDto.Email;
                business.AdminComment = businessDto.AdminComment;
                business.CreatedOnUtc = businessDto.CreatedOnUtc;
                business.LastActivityDateUtc = businessDto.LastActivityDateUtc;
                business.PhoneNumber = businessDto.PhoneNumber;
                business.FaxNumber = businessDto.FaxNumber;
                business.AddressID = businessDto.AddressID;

                return business;
            }
            return null;
        }

        public ResponseData<Domain.Business> UpdateBusiness(Domain.Business business)
        {
            var response = new ResponseData<Domain.Business>();

            var businessDto = Context.Get<BusinessDto>(b => b.ID == business.ID).ToList().FirstOrDefault();
            if (businessDto != null)
            {
                var businessValidator = new BusinessValidation();
                ValidationResult validationResult = businessValidator.Validate(business);
                if (validationResult.IsValid)
                {
                    using (TransactionScope scope= new TransactionScope())
                    {
                        AddressDto addressDto = null;
                        if (businessDto.AddressID == 1)
                        {
                            addressDto = new AddressDto();
                            UpdateBusinessAddress(business, addressDto);
                            addressDto.CreatedOnUtc = DateTime.UtcNow;
                            Context.Repository<AddressDto>().Add(addressDto);
                            businessDto.Address = addressDto;
                        }
                        else
                        {
                            addressDto = businessDto.Address;
                            UpdateBusinessAddress(business, addressDto);
                            Context.Repository<AddressDto>().Update(addressDto);
                        }

                        businessDto.BusinessName = business.BusinessName;
                        businessDto.AdminComment = business.AdminComment;
                        businessDto.LastActivityDateUtc = DateTime.UtcNow;
                        businessDto.PhoneNumber = business.PhoneNumber;
                        businessDto.FaxNumber = business.FaxNumber;

                        Context.Repository<BusinessDto>().Update(businessDto);

                        var userDto = Context.Get<UserProfileDto>(u => u.BusinessID == business.ID && u.IsBusinessAdmin.Value).ToList().FirstOrDefault();
                        userDto.FirstName = business.BusinessName;
                        userDto.FaxNumber = business.FaxNumber;
                        userDto.PhoneNumber = business.PhoneNumber;

                        Context.Repository<UserProfileDto>().Update(userDto);

                        Context.Commit();
                        scope.Complete();
                    }
                    response.Data = GetBusinessById(business.ID);
                    return response;
                }

                response.Error = validationResult.ErrorList();
                return response;
            }
            response.Error = new[] { "Invalid business ID." };
            return response;
        }

        private void UpdateBusinessAddress(Domain.Business business, AddressDto addressDto)
        {
            var address = business.Address;
            addressDto.Address1 = address.Address1;
            addressDto.Address2 = address.Address2;
            addressDto.City = address.City;
            addressDto.CountryID = address.CountryID;
            addressDto.PostalCode = address.PostalCode;
            addressDto.State = address.State;
        }

        private bool UserExist(RegisterUserProfile userProfile)
        {
            var uEmail = userProfile.Email.ToUpper();
            var list = Context.Get<UserProfileDto>(b => b.Email == uEmail).ToList();

            return list.Any();
        }

        private bool BusinessExist(string email)
        {
            BusinessDto business = Context.Get<BusinessDto>(b => b.Email.ToUpper() == email.ToUpper()).ToList()
                    .FirstOrDefault();

            return business != null;
        }
    }
}